﻿#if UNITY_EDITOR
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
/// <summary>
/// SFCSharp代码编译器
/// </summary>
public sealed class ESF_CSharpCompiler
{
    /// <summary>
    /// 静态方法映射
    /// Key:HashCode值
    /// Value:方法
    /// </summary>
    static Dictionary<int, MethodInfo> mCompilerInvokeStaticMethodMaping = new Dictionary<int, MethodInfo>();
    /// <summary>
    /// 编译CSharp代码执行静态方法
    /// </summary>
    /// <param name="_codeTemplete">代码模板</param>
    /// <param name="_className">类名</param>
    /// <param name="_methodName">静态方法名</param>
    /// <param name="_methodParameters">方法参数</param>
    /// <returns>方法返回值</returns>
    public static object CompilerInvokeStaticMethod(string _codeTemplete, string _className, string _methodName, object[] _methodParameters)
    {
        object result = null;
        if (!string.IsNullOrEmpty(_codeTemplete))
        {
            int methodKey = (_codeTemplete + _className + _methodName).GetHashCode();
            MethodInfo method = null;
            if (!mCompilerInvokeStaticMethodMaping.TryGetValue(methodKey, out method))
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                CodeDomProvider provider = CodeDomProvider.CreateProvider(enESF_CodeProviderLanguage.CSharp.ToString());
                if (provider != null)
                {
                    CompilerParameters objCompilerParameters = OnGetCompilerParameters();
                    string code = _codeTemplete;
                    CompilerResults cr = provider.CompileAssemblyFromSource(objCompilerParameters, code);
                    StringBuilder sbLog = new StringBuilder();
                    if (cr.Errors.HasErrors)
                    {
                        sbLog.AppendLine("编译错误：");
                        foreach (CompilerError err in cr.Errors)
                        {
                            sbLog.AppendLine(err.ErrorText);
                        }
                        sbLog.AppendLine(code);
                        ESF_P.ESF_Error(sbLog.ToString());
                    }
                    else
                    {
                        // 通过反射，调用HelloWorld的实例
                        Assembly objAssembly = cr.CompiledAssembly;
                        Type type = objAssembly.GetType(_className);
                        method = type.GetMethod(_methodName);
                        mCompilerInvokeStaticMethodMaping.Add(methodKey, method);
                    }
                }
            }
            if (method != null)
            {
                result = method.Invoke(null, _methodParameters);
            }
        }
        return result;
    }

    /// <summary>
    /// 获得编译参数
    /// </summary>
    /// <returns>编译参数</returns>
    static CompilerParameters OnGetCompilerParameters()
    {
        CompilerParameters objCompilerParameters = new CompilerParameters();
        //objCompilerParameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
        objCompilerParameters.GenerateExecutable = false;
        objCompilerParameters.GenerateInMemory = true;

        List<string> assemblyLocations = new List<string>();
        OnResolverReferencedAssemblies(Assembly.GetCallingAssembly(), ref assemblyLocations, mExceptDll);
        objCompilerParameters.ReferencedAssemblies.AddRange(assemblyLocations.ToArray());
        return objCompilerParameters;
    }

    /// <summary>
    /// 方法映射
    /// Key:HashCode值
    /// Value:方法
    /// </summary>
    static Dictionary<int, MethodInfo> mCompilerInvokeMethodMaping = new Dictionary<int, MethodInfo>();
    /// <summary>
    /// 方法映射
    /// Key:HashCode值
    /// Value:对象
    /// </summary>
    static Dictionary<int, object> mCompilerObjectMaping = new Dictionary<int, object>();

    /// <summary>
    /// 排除的dll
    /// </summary>
    readonly static List<string> mExceptDll = new List<string>() { "mscorlib" };
    /// <summary>
    /// 编译代码
    /// </summary>
    /// <param name="_codeTemplete">代码模板</param>
    /// <param name="_data">数据</param>
    /// <returns>代码</returns>
    public static string Compiler<T>(string _codeTemplete, T _data)
    {
        string result = string.Empty;
        if (!string.IsNullOrEmpty(_codeTemplete))
        {
            int methodKey = _codeTemplete.GetHashCode();
            MethodInfo method = null;
            object methodObject = null;
            if (!mCompilerInvokeMethodMaping.TryGetValue(methodKey, out method))
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                CodeDomProvider provider = CodeDomProvider.CreateProvider(enESF_CodeProviderLanguage.CSharp.ToString());
                if (provider != null)
                {
                    List<ESF_CSharpResult> codes = new List<ESF_CSharpResult>();
                    ESF_CSharpTempleteParser templete = new ESF_CSharpTempleteParser();
                    codes = templete.Resolver<ESF_CSharpResult>(_codeTemplete);
                    StringWriter codeBlock = new StringWriter();
                    List<string> inlineFunction = new List<string>();
                    List<string> usingReference = new List<string>();
                    StringWriter writer = new StringWriter();
                    foreach (ESF_CSharpResult key in codes)
                    {
                        switch (key.classify)
                        {
                            case enESF_CodeResult.CodeBlock:
                                codeBlock.WriteLine(key.code);
                                break;
                            case enESF_CodeResult.InlineFunction:
                                inlineFunction.Add(key.code);
                                break;
                            case enESF_CodeResult.Using:
                                usingReference.Add(key.code);
                                break;
                        }
                    }
                    string className = "SFCSharpCompiler_Dynamic";
                    string methodName = "DynamicCompiler";
                    writer.WriteLine(OnCombineCode(codeBlock.ToString(), usingReference, inlineFunction, _data, className, methodName));

                    CompilerParameters objCompilerParameters = OnGetCompilerParameters();
                    string code = writer.ToString();
                    CompilerResults cr = provider.CompileAssemblyFromSource(objCompilerParameters, code);
                    StringBuilder sbLog = new StringBuilder();
                    if (cr.Errors.HasErrors)
                    {
                        sbLog.AppendLine("编译错误：");
                        foreach (CompilerError err in cr.Errors)
                        {
                            sbLog.AppendLine(err.ErrorText);
                        }
                        sbLog.AppendLine(code);
                        ESF_P.ESF_Error(sbLog.ToString());
                    }
                    else
                    {
                        // 通过反射，调用HelloWorld的实例
                        Assembly objAssembly = cr.CompiledAssembly;
                        methodObject = objAssembly.CreateInstance(className);
                        Type type = methodObject.GetType();
                        method = type.GetMethod(methodName);
                        mCompilerInvokeMethodMaping.Add(methodKey, method);
                        mCompilerObjectMaping.Add(methodKey, methodObject);
                    }
                }
            }
            if (method != null)
            {
                object value = method.Invoke(mCompilerObjectMaping[methodKey], new dynamic[] { _data, typeof(T) });
                if (value != null)
                {
                    result = value.ToString();
                }
                result = result.ESF_ClearRepeatCRLF();
            }
        }
        return result;
    }

    /// <summary>
    /// 合并代码
    /// </summary>
    /// <param name="_codeBlock">代码块</param>
    /// <param name="_usingReference">using引用</param>
    /// <param name="_inlineFunctions">内联函数</param>
    /// <param name="_data">数据</param>
    /// <param name="_className">类名</param>
    /// <param name="_methodName">方法名</param>
    /// <returns>代码</returns>
    static string OnCombineCode<T>(string _codeBlock, List<string> _usingReference, List<string> _inlineFunctions, T _data, string _className, string _methodName)
    {
        List<string> defaultUsing = new List<string>() {
            "using System.IO;","using System;","using UnityEngine;","using System.Reflection;"
        };

        StringWriter writer = new StringWriter();

        #region defaultUsing
        if (defaultUsing != null)
        {
            foreach (string key in defaultUsing)
            {
                writer.WriteLine(key);
            }
        }
        #endregion

        #region usingReference
        if (_usingReference != null)
        {
            foreach (string key in _usingReference)
            {
                if (!defaultUsing.Contains(key))
                {
                    writer.WriteLine(key);
                }
            }
        }
        #endregion

        writer.WriteLine(string.Format("public class {0}", _className));
        writer.WriteLine("{");//class start

        #region inlineFunctions
        if (_inlineFunctions != null)
        {
            foreach (string func in _inlineFunctions)
            {
                writer.WriteLine(func);
            }
        }
        #endregion

        #region method
        string formalParamName = "_data";
        writer.WriteLine(string.Format("public string {0} (object {1},Type _type)", _methodName, formalParamName));
        writer.WriteLine("{");
        writer.WriteLine("");
        writer.WriteLine("StringWriter writer = new StringWriter();");

        Type type = typeof(T);
        if (type.IsAnonymousType())
        {
            List<string> objectCode = new List<string>();
            List<string> paramNames = OnResolverAnonymousObject(_data, formalParamName, "", ref objectCode);
            foreach (string p in paramNames)
            {
                writer.WriteLine(p);
            }
            writer.WriteLine("dynamic @Model = new {" + objectCode.ESF_Join(enESF_SeparatorSymbol.Comma) + "};");
        }
        else
        {
            writer.WriteLine(string.Format("dynamic @Model = {0};", formalParamName));
        }
        writer.WriteLine(_codeBlock);
        writer.WriteLine("return writer.ToString();");
        writer.WriteLine("}");
        #endregion

        writer.WriteLine("}"); ////class end
        return writer.ToString();
    }

    /// <summary>
    /// 解析匿名对象
    /// </summary>
    /// <param name="_data">对象</param>
    /// <param name="_objectName">对象名称</param>
    /// <param name="_prefixName">前缀名称</param>
    /// <param name="_objectCode">对象代码</param>
    /// <returns>匿名对象参数代码</returns>
    static List<string> OnResolverAnonymousObject(object _data, string _objectName, string _prefixName, ref List<string> _objectCode)
    {
        List<string> paramCodes = new List<string>();
        Type type = _data.GetType();
        PropertyInfo[] pts = type.GetProperties();
        if (pts != null)
        {
            object value = null;
            string prefix = string.Empty;
            foreach (PropertyInfo p in pts)
            {
                value = p.GetValue(_data);
                prefix = _prefixName + "_" + p.Name;
                paramCodes.Add(string.Format("dynamic {0} = {2}.GetType().GetProperty(\"{1}\").GetValue({2});", prefix, p.Name, _objectName));

                if (value.GetType().IsAnonymousType())
                {
                    List<string> codes = new List<string>();
                    paramCodes.AddRange(OnResolverAnonymousObject(value, prefix, prefix, ref codes));
                    _objectCode.Add(p.Name + " = new {" + codes.ESF_Join(enESF_SeparatorSymbol.Comma) + "}");
                }
                else
                {
                    _objectCode.Add(string.Format("{0} = {1}", p.Name, prefix));
                }
            }
        }
        return paramCodes;
    }

    /// <summary>
    /// 解析引用程序集
    /// </summary>
    /// <param name="_referAssembly">引用程序集</param>
    /// <param name="_assemblyLocations">引用的程序集组</param>
    /// <param name="_exceptDll">排除的dll</param>
    static void OnResolverReferencedAssemblies(Assembly _referAssembly, ref List<string> _assemblyLocations,List<string> _exceptDll)
    {
        string assemblyLocation = _referAssembly.Location;
        if (!_assemblyLocations.Contains(assemblyLocation))
        {
            if (!_assemblyLocations.Contains(assemblyLocation))
            {
                _assemblyLocations.Add(assemblyLocation);
            }            
            AssemblyName[] ans = _referAssembly.GetReferencedAssemblies();
            if (ans != null)
            {
                foreach (AssemblyName n in ans)
                {
                    if (!_exceptDll.Contains(n.Name))
                    {
                        Assembly assembly = Assembly.Load(n);
                        if (!_assemblyLocations.Contains(assembly.Location))
                        {
                            _assemblyLocations.Add(assembly.Location);
                        }
                    }
                }
            }
        }
    }
}
#endif
